////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//                                                                                                                            //
//                                                         WNProject                                                          //
//                                                                                                                            //
//            This file is distributed under the BSD 2-Clause open source license. See License.txt for details.               //
//                                                                                                                            //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#pragma once

#ifndef __WN_SCRIPTING_INSTRUCTION_H__
#define __WN_SCRIPTING_INSTRUCTION_H__

#include "WNScripting/inc/WNParseNode.h"

#include <deque>


namespace WNScripting {

    enum WNInstructionType {
        eWNInstructionNull,
        eWNInstructionAssign,
        eWNInstructionList,
        eWNInstructionDeclaration,
        eWNInstructionReturn,
        eWNInstructionConditional,
        eWNInstructionConditionalElse,
        eWNInstructionWhileLoop,
        eWNInstructionDoLoop,
        eWNInstructionForLoop,
        eWNInstructionDelete,
        eWNInstructionExpression
    };

    class WNExpression;
    class WNInstructionList;
    class WNDataType;
    class WNIdentifier;
    class WNLValue;

    class WNInstruction : public WNParseNode {
    public:
        virtual ~WNInstruction();
        WNInstruction(WN_SIZE_T _startLine,
                      WN_SIZE_T _endLine,
                      WN_SIZE_T _startChar,
                      WN_SIZE_T _endChar);

        WNInstruction(WN_SIZE_T _startLine,
                      WN_SIZE_T _endLine,
                      WN_SIZE_T _startChar,
                      WN_SIZE_T _endChar,
                      WNInstructionList* _instructionList);
    
        WNInstruction(WN_SIZE_T _startLine,
                      WN_SIZE_T _endLine,
                      WN_SIZE_T _startChar,
                      WN_SIZE_T _endChar,
                      WNLValue* _lval,
                      WNStaticArithmetic _arithType,
                      WNExpression* _expression);
   
        WNInstruction(WN_SIZE_T _startLine,
                      WN_SIZE_T _endLine,
                      WN_SIZE_T _startChar,
                      WN_SIZE_T _endChar,
                      WNDataType* _type,
                      WNIdentifierElement _identifier,
                      WNLValue* _lvalue,
                      WNExpression* _expression);
    
        WNInstruction(WN_SIZE_T _startLine,
                      WN_SIZE_T _endLine,
                      WN_SIZE_T _startChar,
                      WN_SIZE_T _endChar,
                      WNLValue* _lvalue,
                      WN_UINT32 _deleteLevels);

        WNInstruction(WN_SIZE_T _startLine,
                      WN_SIZE_T _endLine,
                      WN_SIZE_T _startChar,
                      WN_SIZE_T _endChar,
                      WNExpression* _expression,
                      bool _functionCall);

        WNInstruction(WN_SIZE_T _startLine,
                      WN_SIZE_T _endLine,
                      WN_SIZE_T _startChar,
                      WN_SIZE_T _endChar,
                      WNExpression* _expression,
                      WNInstruction* _instruction,
                      WNInstructionType _type);

        WNInstruction(WN_SIZE_T _startLine,
                      WN_SIZE_T _endLine,
                      WN_SIZE_T _startChar,
                      WN_SIZE_T _endChar,
                      WNExpression* _expression,
                      WNInstruction* _instruction,
                      WNInstruction* _elseInstruction);

        WNInstruction(WN_SIZE_T _startLine,
                      WN_SIZE_T _endLine,
                      WN_SIZE_T _startChar,
                      WN_SIZE_T _endChar,
                      WNInstruction* _initial,
                      WNExpression* _condition,
                      WNInstruction* _loopval,
                      WNInstruction* _loopIntern);

        virtual llvm::Value* OptimizeAndGenerate(WNScriptingEngineData& _scriptingData, WNFunctionDecl* _functionDecl);
        virtual WN_BOOL ReportErrors();
        WN_BOOL Returns();

    private:
        WNInstructionType mType;

        union {
            WNInstructionList* mInstructionList;
            WNIdentifierElement mIdentifier;
            WNInstruction* mInstruction;
            WNLValue* mLvalue;
        } mFirstElement;

        union {
            WNExpression* expression;
            WNDataType* type;
            WN_UINT32 number;
        } mSecondElement;

        union {
            WNExpression* expression;
            WNInstruction* mInstruction;
        } mThirdElement;

        union {
            WNInstruction* mInstruction;
            WNLValue* mLvalue;
        } mFourthElement;
        WNStaticArithmetic mArithmeticType;
        std::deque<WNInstruction*> mInstructions;
        WN_BOOL mReturns;
        WN_BOOL mHeapAlloc;
    };

    class WNInstructionList : public WNParseNode {
    public:
        virtual ~WNInstructionList();

        WNInstructionList(WN_SIZE_T startLine,
                          WN_SIZE_T endLine,
                          WN_SIZE_T startChar,
                          WN_SIZE_T endChar);

        WNInstructionList(WN_SIZE_T startLine,
                          WN_SIZE_T endLine,
                          WN_SIZE_T startChar,
                          WN_SIZE_T endChar,
                          WNInstruction* instruction,
                          WNInstructionList* instructionList);

        WNInstruction* AppropriateInstruction() { WNInstruction* elem = mInstruction; mInstruction = WN_NULL; return(elem); }
        WNInstructionList* AppropriateInstructionList() { WNInstructionList * elem = mInstructionList; mInstructionList = WN_NULL; return(elem); }
        virtual llvm::Value* OptimizeAndGenerate(WNScriptingEngineData& scriptingData, WNFunctionDecl* functionDecl);
        virtual WN_BOOL ReportErrors();
        WN_BOOL Returns();

    private:
        WNInstruction* mInstruction;
        WNInstructionList* mInstructionList;
        WN_BOOL mReturns;
    };
}
#endif//__WN_SCRIPTING_INSTRUCTION_H__